//! \file Dice.cpp
//! \brief The Dice class implements the complete dice set (and more) for
//! Dungeons and Dragons game rules.
// -----------------------------------------------------------------------------
//! This program is free software; you can redistribute it and/or
//! modify it under the terms of the GNU General Public License
//! as published by the Free Software Foundation; either version 2
//! of the License, or (at your option) any later version.
//!
//! This program is distributed in the hope that it will be useful,
//! but WITHOUT ANY WARRANTY; without even the implied warranty of
//! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//! GNU General Public License for more details.
//!
//! You should have received a copy of the GNU General Public License
//! along with this program; if not, write to the Free Software Foundation,
//! Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
// -----------------------------------------------------------------------------

#include "Dice.h"
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <time.h>

namespace std {

// =====[ Static Members ]======================================================
Dice* Dice::m_pDice; //!< (Singleton) Instance.


// =====[ Public Methods ]======================================================

// =============================================================================
// Dice::Dice
// -----------------------------------------------------------------------------
//! The class constructor.
// -----------------------------------------------------------------------------
Dice::Dice()
{
  // Seed the randomizer.
  srand(time(NULL));
}


// =============================================================================
// Dice::getSingleton
// -----------------------------------------------------------------------------
//! Returns a pointer to the instanced singleton object.
//!
//! \retval Singleton
//!   A pointer to the instanced singleton object.
//!
//! \b Example: \code
//! int Result = Dice::getSingleton()->roll("1d20");
//! \endcode
// -----------------------------------------------------------------------------
Dice* Dice::getSingleton()
{
  // Only allow one instance of class to be generated.
  if (!m_pDice)
    { m_pDice = new Dice; } // Create our single instance.
  return (m_pDice);         // Finally, return our singleton.
}


// =============================================================================
// Dice::roll
// -----------------------------------------------------------------------------
//! Rolls the requested number of dice (all having the same number of faces)
//! as requested.
//!
//! \param [in] Expression
//!   A string containing the D&D dice expression to evaluate.
//! \param [in] Type
//!   The type of roll to make (i.e. how to evaluate the D&D dice expression).
//!
//! \retval Result
//!   The resulting sum of the dice being rolled.
//!   - ERROR_ROLLING: There was an error rolling the dice.
//!   - Otherwise, the sum of the rolled dice.
//!   .
//!
//! \b Example: \code
//! Result = Dice::getSingleton()->roll("1d20");
//! Result = Dice::getSingleton()->roll("1d20+2");
//! Result = Dice::getSingleton()->roll("(1d6)+(1d4)+2");
//! \endcode
// -----------------------------------------------------------------------------
int Dice::roll(string Expression, eRollType Type)
{
  int Result = ERROR_ROLLING;

  // Validate our arguments.
  if (Expression.empty())
    { return (ERROR_ROLLING); } // An error occurred.

  // Initialize our token parsing.
  m_pExpression = Expression.c_str();
  m_RollType = Type;

  // Get the first token to check.
  getToken();

  // Verify we have gotten a valid token to work with.
  if (m_Token[0] == '\0')
    { return (ERROR_ROLLING); } // An error occurred.

  // Evaluate the expression.
  evaluateLevel1(Result);

  // Last token must be a null string.
  if (m_Token[0] != '\0')
    { return (ERROR_ROLLING); } // An error occurred.

  // Return the roll results.
  return (Result);
}


// =====[ Private Methods ]=====================================================


// =============================================================================
// Dice::roll
// -----------------------------------------------------------------------------
//! Rolls the requested number of dice (all having the same number of faces)
//! as requested.
//!
//! \param [in] Number
//!   The number of dice being rolled.
//! \param [in] Faces
//!   The number of faces each dice being rolled has.
//!
//! \retval Result
//!   The resulting sum of the dice being rolled.
//!   - ERROR_ROLLING: There was an error rolling the dice.
//!   - Otherwise, the sum of the rolled dice.
//!   .
//!
//! \b Example: \code
//! int Result = Dice::getSingleton()->roll(2, 6); // For a 2d6 roll.
//! \endcode
// -----------------------------------------------------------------------------
int Dice::roll(int Number, int Faces)
{
  int Index, Roll, Result = 0;

  // Iterate through each roll.
  for (Index = 0; Index < Number; Index++)
  {
    Roll = (rand() % Faces + 1);  // Make the roll.
    Result+= Roll;                // Accumulate to the total.
  }

  // Return the results.
  return (Result);
}


// =============================================================================
// Dice::getToken
// -----------------------------------------------------------------------------
//! Process the D&D dice expression token by token.  Calling this member will
//! localize the newly gotten token and determine the type of the token.
//! m_Token and m_TokenType will be updated by this member.
// -----------------------------------------------------------------------------
void Dice::getToken(void)
{
  char *pDst;

  // Initialize our copy for the next token.
  m_TokenType = eTOKEN_TYPE_UNKNOWN;      // Initially, the token type is unknown.
  pDst = &(m_Token[0]);                   // Copy the token to our local buffer.
  *pDst = '\0';                           // Initialize the copy to a null string.

  // Check if we're at the end of the expression.
  if (!*m_pExpression)
    { return; }                           // Nothing left to scan, exit.

  // Skip over any whitespace that we might have.
  while (isspace(*m_pExpression))
    { ++m_pExpression; }

  // Determine the type of token we currently have.
  if (isDelimiter(*m_pExpression))
  {
    m_TokenType = eTOKEN_TYPE_DELIMITER;  // Update the token type
    *pDst++ = *m_pExpression++;           // Copy a character.
  }
  else if (isalpha(*m_pExpression))
  {
    // Copy the remainder of the token to the destination.
    while (!isDelimiter(*m_pExpression))
      { *pDst++ = *m_pExpression++; }     // Copy a character.
    m_TokenType = eTOKEN_TYPE_VARIABLE;   // Update the token type
  }
  else if (isdigit(*m_pExpression))
  {
    while (!isDelimiter(*m_pExpression))
      { *pDst++ = *m_pExpression++; }     // Copy a character.
    m_TokenType = eTOKEN_TYPE_NUMBER;     // Update the token type
  }

  // Append null termination to the token string.
  *pDst = '\0';
}


// =============================================================================
// Dice::isDelimiter
// -----------------------------------------------------------------------------
//! Determines if the current token is a functional character (delimiter) or
//! not.
//!
//! \param [in] Value
//!   The value of the token character being evaluate as a delimiter.
//!
//! \retval Exception
//!   The success status of the requested operation.
//!   - true: The requested Value is a token.
//!   - false: The requested Value is not a token.
//!   .
// -----------------------------------------------------------------------------
bool Dice::isDelimiter(char Value)
{
  if (strchr(" +-/*()dD", Value))
    { return (true); }
  if ((Value == 9) || (Value == '\r') || (Value == 0))
    { return (true); }
  return (false);
}


// =============================================================================
// Dice::evaluateLevel1
// -----------------------------------------------------------------------------
//! Evaluates a level 1 token of the expression.  Level 1 adds or subtracts
//! two terms.
//!
//! \param [out] Result
//!   The resulting value after the evaluation.
// -----------------------------------------------------------------------------
void Dice::evaluateLevel1(int &Result)
{
  char Operator;
  int Temp;

  evaluateLevel2(Result);       // Check the next level.
  while ((Operator = *m_Token) == '+' || Operator == '-')
  {
    getToken();                 // Get the token to check.
    evaluateLevel2(Temp);       // Check the next level.
    switch (Operator)
    {
      case '-':
        Result = Result - Temp; // Add the values.
        break;
      case '+':
        Result = Result + Temp; // Subtract the values.
        break;
    }
  }
}


// =============================================================================
// Dice::evaluateLevel2
// -----------------------------------------------------------------------------
//! Evaluates a level 2 token of the expression.  Level 1 multiplies or divides
//! two terms.
//!
//! \param [out] Result
//!   The resulting value after the evaluation.
// -----------------------------------------------------------------------------
void Dice::evaluateLevel2(int &Result)
{
  char Operator;
  int Temp;

  evaluateLevel3(Result);             // Check the next level.
  while (((Operator = *m_Token)) == '*' || (Operator == '/'))
  {
    getToken();                       // Get the token to check.
    evaluateLevel3(Temp);             // Check the next level.
    switch (Operator)
    {
      case '*':
        Result = Result * Temp;       // Multiply the values.
        break;
      case '/':
        if (Temp == 0)                // Avoid division by 0.
          { Result = ERROR_ROLLING; } // An error occurred.
        else
          { Result = Result / Temp; } // Divide the values.
        break;
    }
  }
}


// =============================================================================
// Dice::evaluateLevel3
// -----------------------------------------------------------------------------
//! Evaluates a level 3 token of the expression.  Level 3 rolls the dice.
//!
//! \param [out] Result
//!   The resulting value after the evaluation.
// -----------------------------------------------------------------------------
void Dice::evaluateLevel3(int &Result)
{
  char Operator;
  int Temp;

  evaluateLevel4(Result);               // Check the next level.
  while ((Operator = *m_Token) == 'd')
  {
    getToken();                         // Get the token to check.
    evaluateLevel4(Temp);               // Check the next level.
    if ((Operator == 'D') || (Operator == 'd'))
    {
      switch (m_RollType)
      {
        case eROLL_VALUE:
          Result = roll(Result, Temp);  // Roll the value.
          break;
        case eROLL_MINIMUM:
          Result = Result;              // Calculate the minimum value.
          break;
        case eROLL_MAXIMUM:
          Result = Result * Temp;       // Calculate the maximum value.
          break;
      }
    }
  }
}


// =============================================================================
// Dice::evaluateLevel4
// -----------------------------------------------------------------------------
//! Evaluates a level 4 token of the expression.  Level 4 handles tokens that
//! are contained in parenthesis.
//!
//! \param [out] Result
//!   The resulting value after the evaluation.
// -----------------------------------------------------------------------------
void Dice::evaluateLevel4(int &Result)
{
  if ((*m_Token == '('))
  {
    getToken();                         // Get the token to check.
    evaluateLevel1(Result);             // Check the next level.
    if(*m_Token != ')')
      { Result = ERROR_ROLLING; }       // An error occurred.
    getToken();                         // Get the token to check.
  }
  else
    { evaluateLevelFinal(Result); }     // Evaluate the final level.
}


// =============================================================================
// Dice::evaluateLevel4
// -----------------------------------------------------------------------------
//! Evaluates the final level of a token of the expression.  The final level
//! converts the token to an integer.
//!
//! \param [out] Result
//!   The resulting value after the evaluation.
// -----------------------------------------------------------------------------
void Dice::evaluateLevelFinal(int &Result)
{
  switch (m_TokenType)
  {
    case eTOKEN_TYPE_NUMBER:
      Result = atoi(m_Token); // Convert the token to a number.
      getToken();             // Get the token to check.
      return;
    default:
      Result = ERROR_ROLLING; // An error occurred.
  }
}


} // namespace std
